﻿using Microsoft.Practices.Unity;
using Microsoft.VisualStudio.Shell;
using Sasa.QualityTools.CodeAnalysis.Common;
using Sasa.QualityTools.CodeAnalysis.Core.Views;
using Sasa.QualityTools.CodeAnalysis.Core.VisualStudio;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Commands;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Options;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.ViewModels;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.Views;
using Sasa.QualityTools.CodeAnalysis.FxCopIntegrator.VisualStudio;
using System;
using System.ComponentModel.Design;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Controls;

namespace Sasa.QualityTools.CodeAnalysis.FxCopIntegrator
{
    [PackageRegistration(UseManagedResourcesOnly = true)]
    [ProvideAutoLoad(Constants.UIContext.SolutionExists)]
    [InstalledProductRegistration("#110", "#112", CommonConstants.ProductVersion, IconResourceID = 400)]
    [ProvideMenuResource("Menus.ctmenu", 1)]
    [ProvideToolWindow(typeof(CodeInspectionResultWindowPane), Style = VsDockStyle.MDI, Height = 400, Width = 800, Transient = true)]
    [ProvideToolWindowVisibility(typeof(CodeInspectionResultWindowPane), Constants.UIContext.SolutionExists)]
    [ProvideToolWindow(typeof(CodeMetricsCalculationResultWindowPane), Style = VsDockStyle.MDI, Height = 400, Width = 800, Transient = true)]
    [ProvideToolWindowVisibility(typeof(CodeMetricsCalculationResultWindowPane), Constants.UIContext.SolutionExists)]
    [ProvideToolWindow(typeof(RuleDetailWindowPane), Orientation = ToolWindowOrientation.Right, Style = VsDockStyle.Tabbed, Window = Constants.Window.SolutionExplorer, Transient = true)]
    [ProvideToolWindowVisibility(typeof(RuleDetailWindowPane), Constants.UIContext.SolutionExists)]
    [ProvideOptionPageAttribute(typeof(GeneralOptionPageData), "FxCop Integrator", "General", 1001, 1002, true)]
    [ProvideProfileAttribute(typeof(GeneralOptionPageData), "FxCop Integrator", "General", 1001, 1002, true, DescriptionResourceID = 1003)]
    [ProvideBindingPath]
    [Guid(Constants.Package.Id)]
    public sealed class FxCopIntegratorPackage : Package, IFxCopIntegratorPackage
    {
        private IUnityContainer container;


        public GeneralOption GeneralOption
        {
            get
            {
                return new GeneralOption((GeneralOptionPageData)GetDialogPage(typeof(GeneralOptionPageData)));
            }
        }


        public void ShowGeneralOptionDialog()
        {
            ShowOptionPage(typeof(GeneralOptionPageData));
        }

        protected override void Initialize()
        {
            Logger.Register(new ActivityLogger());
            Logger.Register(TraceLogger.Current);

            Logger.Info("Initializing FxCop Integrator package.");

            base.Initialize();

            var mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService;
            mcs.AddCommand(BuildUpMenuCommand<AnalyzeSolutionCommand>());
            mcs.AddCommand(BuildUpMenuCommand<AnalyzeProjectCommand>());
            mcs.AddCommand(BuildUpMenuCommand<AnalyzeMultiProjectCommand>());
            mcs.AddCommand(BuildUpMenuCommand<InspectSolutionCommand>());
            mcs.AddCommand(BuildUpMenuCommand<InspectProjectCommand>());
            mcs.AddCommand(BuildUpMenuCommand<InspectMultiProjectCommand>());
            mcs.AddCommand(BuildUpMenuCommand<InspectItemsCommand>());
            mcs.AddCommand(BuildUpMenuCommand<CalculateSolutionMetricsCommand>());
            mcs.AddCommand(BuildUpMenuCommand<CalculateProjectMetricsCommand>());
            mcs.AddCommand(BuildUpMenuCommand<CalculateMultiProjectMetricsCommand>());
            mcs.AddCommand(BuildUpMenuCommand<ShowCodeAnalysisResultCommand>());
            mcs.AddCommand(BuildUpMenuCommand<ShowCodeMetricsCalculationResultCommand>());
            mcs.AddCommand(BuildUpMenuCommand<ConfigureSolutionCommand>());
            mcs.AddCommand(BuildUpMenuCommand<ConfigureProjectCommand>());
            mcs.AddCommand(BuildUpMenuCommand<EditFxCopDictionaryCommand>());
            mcs.AddCommand(BuildUpMenuCommand<ShowMoreRuleDetailCommand>());

            container = new UnityContainer();
            container.RegisterInstance<IFxCopIntegratorPackage>(this);
            container.RegisterInstance<IIde>(new Ide(this));
            container.RegisterType<ICodeAnalysisProgressView, CodeAnalysisProgressWindow>();
            container.RegisterType<ICodeAnalysisConfigurationView, SolutionConfigurationEditorWindow>("solution");
            container.RegisterType<ICodeAnalysisConfigurationView, ProjectConfigurationEditorWindow>("project");
            container.RegisterType<IFxCopDictionaryView, FxCopDictionaryEditorWindow>();
            container.RegisterType<IErrorReportView, ErrorReportWindow>();

            var metadata = new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.Inherits, new PropertyChangedCallback(OnDataContextChanged));
            FrameworkElement.DataContextProperty.AddOwner(typeof(DataGridColumn));
            FrameworkElement.DataContextProperty.OverrideMetadata(typeof(DataGrid), metadata);

            if (!File.Exists(Core.Constants.CodeAnalysisCommandPath))
            {
                if (!Directory.Exists(Core.Constants.AppDataDir))
                {
                    Directory.CreateDirectory(Core.Constants.AppDataDir);
                }
                var currentDir = Path.GetDirectoryName(GetType().Assembly.Location);
                var files = new string[] { Core.Constants.CodeAnalysisCommandFileName, "Sasa.QualityTools.CodeAnalysis.Common.dll", "Sasa.QualityTools.CodeAnalysis.Core.dll" };
                files.ToList().ForEach(o => File.Copy(Path.Combine(currentDir, o), Path.Combine(Core.Constants.AppDataDir, o)));
            }
        }

        private static void OnDataContextChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
        {
            DataGrid dataGrid = sender as DataGrid;
            if (dataGrid == null)
            {
                return;
            }

            foreach (DataGridColumn column in dataGrid.Columns)
            {
                column.SetValue(FrameworkElement.DataContextProperty, e.NewValue);
            }
        }

        [SuppressMessage("Microsoft.Design", "CA1031")]
        private MenuCommand BuildUpMenuCommand<TCommand>()
            where TCommand : ICommand
        {
            var attribute = (MenuCommandAttribute)Attribute.GetCustomAttribute(typeof(TCommand), typeof(MenuCommandAttribute));
            return new MenuCommand(delegate
            {
                try
                {
                    TraceLogger.Current.Clear();
                    var command = (TCommand)Activator.CreateInstance(typeof(TCommand), container);
                    command.Execute();
                }
                catch (Exception ex)
                {
                    ShowErrorReportWindow(ex);
                }
            },
            new CommandID(new Guid(attribute.MenuGroupGuid), attribute.CommandId));
        }

        private void ShowErrorReportWindow(Exception error)
        {
            try
            {
                Logger.Error(error, "FxCop Integrator has encountered a problem.");

                var ide = container.Resolve<IIde>();

                var model = new ErrorReportViewModel();
                model.VisualStudioVersion = ide.VersionInfo;
                model.Error = error;
                var view = container.Resolve<IErrorReportView>();
                view.DataContext = model;
                model.ShowWebPageRequired += (sender, e) =>
                {
                    ide.Navigate(e.Url);
                };
                model.CloseRequired += delegate
                {
                    view.Close();
                };
                view.Show();
            }
            catch (Exception ex)
            {
                Logger.Error(ex, "Failed to send bug report because:");
                throw;
            }
        }
    }
}
